#include "grate_oracle.h"
#include <cstring>
#include <cmath>
#include "grate_constants.h"

double calc_grate_oracle(dfa * DFA, const input_data & INPUT, const grate_config & CONFIG){
  int i, j, k;
  char h;
  int total = DFA->total;
  int alph_size = DFA->alph_size;
  double * cnt_old = new double [total+1];
  memset(cnt_old, 0, sizeof(double) * (total + 1));
  if (CONFIG.OUTPUT_PROGRESS){
    INPUT.dump("old counters allocated\n");
  }
  double * cnt_new = new double [total+1];
  memset(cnt_new, 0, sizeof(double) * (total + 1));
  if (CONFIG.OUTPUT_PROGRESS){
    INPUT.dump("new counters allocated\n");
  }
  cnt_old[1] = 1;
  double pcomp = 1, ccomp, last_loc_max = 0, last_loc_min = 0;
  double * rates = new double [CONFIG.MAXITER];
  for (i=0; i<CONFIG.MAXITER; i++){
    for (j=1; j<=total; j++){
      for (h=0; h<alph_size; h++){
        k = (*DFA)[j][h];
        if (!k) continue;
        cnt_new[k] += cnt_old[j];
      }
    }
    ccomp = 0;
    for (j=1; j<=total; j++){
      if (CONFIG.ADD_LOOPS) cnt_new[j] += cnt_old[j];
      ccomp += cnt_new[j];
      cnt_old[j] = cnt_new[j];
      cnt_new[j] = 0;
    }
    rates[i] = ccomp / pcomp;
    // ORACLE HERE
    if (i >= MINITER){
      if ((rates[i-1] >= rates[i-2]) && (rates[i-1] >= rates[i])) last_loc_max = rates[i-1];
      if ((rates[i-1] <= rates[i-2]) && (rates[i-1] <= rates[i])) last_loc_min = rates[i-1];
      if (fabs(rates[i] - rates[i-1]) < INPUT.delta){
        // Oscillating
        if ((last_loc_max > 0) && (last_loc_min > 0)){
          if (last_loc_max - last_loc_min < INPUT.delta){
            if (CONFIG.OUTPUT_PROGRESS){
              INPUT.dump("Oscillating sequence! The number of iterations = %d\n", i);
            }
            if (GUI_VERSION){
              printf("%d\n", i);
            }
            double res = rates[i] - (CONFIG.ADD_LOOPS == true);
            delete[] rates;
            delete[] cnt_old;
            delete[] cnt_new;
            return res;
          }
        }
        // Monotonic
        else{
          if (CONFIG.OUTPUT_PROGRESS){
            INPUT.dump("Monotonic sequence! The number of iterations = %d\n", i);
          }
          if (GUI_VERSION){
            printf("%d\n", i);
          }
          double q = pow(fabs(rates[i]-rates[i-1]), 1.0 / i);
          double sum = (rates[i] - rates[i-1]) / (1 - q);
          double res = rates[i-1] + sum - (CONFIG.ADD_LOOPS == true);
          delete[] rates;
          delete[] cnt_old;
          delete[] cnt_new;
          return res;
        }
      }
    }
    pcomp = ccomp;
  }
  if (CONFIG.OUTPUT_PROGRESS){
    INPUT.dump("\nNot enough iterations! Current difference is %.20lf\n\n", rates[i-1] - rates[i-2]);
  }
  if (GUI_VERSION) printf("-1\n");
  double res = rates[i-1] - (CONFIG.ADD_LOOPS == true);
  delete[] rates;
  delete[] cnt_old;
  delete[] cnt_new;
  return res;
}
